﻿/*
 file: event.js
 dependsOn: core.js, browsers.js, element.js
 */

/**
 * This object wraps native event objects and provides simple,
 * efficient, cross-browser methods for querying and manipulating them. 
 * @since 0.1
 */
$.Event = window.Event || {};

  
var timer, isButton = 
  $.IE ? function(event, code) {
    return event.button == { 0: 1, 1: 4, 2: 2 }[code];
  } 
  : $.WebKit ? function(event, code) {
    switch (code) {
      case 0: return event.which == 1 && !event.metaKey;
      case 1: return event.which == 1 && event.metaKey;
      default: return false;
    }
  } 
  : function(event, code) {
    return event.which ? (event.which === code + 1) : (event.button === code);
  },

methods = {
  isLeftClick:   function() { return isButton(this, 0) },
  isMiddleClick: function() { return isButton(this, 1) },
  isRightClick:  function() { return isButton(this, 2) },
  
  element: function() {
    this = Event.extend(this);
    var node = this.target, currentTarget = this.currentTarget;
    
    if ($.Gecko && currentTarget && currentTarget.tagName && 
        (['load', 'error'].include(type) ||
        (currentTarget.tagName.toLowerCase() === "input" && 
        currentTarget.type === "radio" && this.type === "click"))
      node = currentTarget;
    
    return $(node && node.nodeType == Node.TEXT_NODE ?
      node.parentNode : node);
  },

  findElement: function(expression) {
    var element = Event.element(this);
    if (!expression) return element;
    var elements = [element].concat(element.ancestors());
    return Selector.findElement(elements, expression, 0);
  },

  pointer: function() {
    var docElement = document.documentElement,
    body = document.body || { scrollLeft: 0, scrollTop: 0 };
    return {
      x: this.pageX || (this.clientX + 
        (docElement.scrollLeft || body.scrollLeft) -
        (docElement.clientLeft || 0)),
      y: this.pageY || (this.clientY + 
        (docElement.scrollTop || body.scrollTop) -
        (docElement.clientTop || 0))
    };
  },

  relatedTarget: function() {
    var type = this.type, element = 
      type == 'mouseover' ? this.fromElement : 
      type == 'mouseout' ? this.toElement : null;
    return $(element);
  },

  stop: function() {
    Event.extend(this);
    this.preventDefault();
    this.stopPropagation();
    this.stopped = true;
  }
};

if($.IE) {
  $.extend(methods, {  
    _extendedByPrototype: true,    
    stopPropagation     : function() { this.cancelBubble = true },    
    preventDefault      : function() { this.returnValue = false },    
    inspect             : function() { return "[object Event]" },    
    target              : $(this.srcElement),    
    pageX               : this.pointer().x,    
    pageY               : this.pointer().y
  });  
  Event.extend = function(event) {
    if (!event || event._extendedByPrototype) return event;
    return Object.extend(event, methods);
  };
}
else {
  Event.prototype = Event.prototype || 
      document.createEvent("HTMLEvents")['__proto__'];
  Object.extend(Event.prototype, methods);
  Event.extend = $.IdFn;
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,
  
  cache: { },

  observe: function(element, eventName, handler) {
    element = $(element);
    var name = getDOMEventName(eventName);
    
    var wrapper = createWrapper(element, eventName, handler);
    if (!wrapper) return element;
    
    if (element.addEventListener) {
      element.addEventListener(name, wrapper, false);
    } else {
      addEventDispatcher(element, eventName);
    }
    
    return element;
  },

  stopObserving: function(element, eventName, handler) {
    element = $(element);
    eventName = Object.isString(eventName) ? eventName : null;
    var id = getEventID(element), c = cache[id];

    if (!c) {
      return element;
    }
    else if (!handler && eventName) {
      getWrappersForEventName(id, eventName).each(function(wrapper) {
        Event.stopObserving(element, eventName, wrapper.handler);
      });
      return element;
    }
    else if (!eventName) {
      Object.keys(c).without("element").each(function(eventName) {
        Event.stopObserving(element, eventName);
      });
      return element;
    }
    
    var wrapper = findWrapper(id, eventName, handler);
    if (!wrapper) return element;
    
    var name = getDOMEventName(eventName);
    if (element.removeEventListener) {
      element.removeEventListener(name, wrapper, false);
      destroyWrapper(id, eventName, handler); 
    } else {
      destroyWrapper(id, eventName, handler);
      var wrappers = getWrappersForEventName(id, eventName); 
      if (!wrappers.length) { 
        element.detachEvent("on" + name, wrappers.dispatcher); 
        wrappers.dispatcher = null; 
      } 
    }
    
    return element;
  },

  fire: function(element, eventName, memo) {
    element = $(element);
    if (element == document && document.createEvent && !element.dispatchEvent)
      element = document.documentElement;
      
    var event;
    if (document.createEvent) {
      event = document.createEvent("HTMLEvents");
      event.initEvent("dataavailable", true, true);
    } else {
      event = document.createEventObject();
      event.eventType = "ondataavailable";
    }

    event.eventName = eventName;
    event.memo = memo || { };

    if (document.createEvent) {
      element.dispatchEvent(event);
    } else {
      element.fireEvent(event.eventType, event);
    }

    return Event.extend(event);
  }
});

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Event.fire.methodize(),
  observe:       Event.observe.methodize(),
  stopObserving: Event.stopObserving.methodize(),
  loaded:        false
});


function fireContentLoadedEvent() {
  if (document.loaded) return;
  if (timer) window.clearInterval(timer);
  document.fire("dom:loaded");
  document.loaded = true;
}

if (document.addEventListener) 
  document.addEventListener("DOMContentLoaded", fireContentLoadedEvent, false);
else {
  document.attachEvent("onreadystatechange", function() {
    if (document.readyState == "complete") {
      document.detachEvent("onreadystatechange", arguments.callee);
      fireContentLoadedEvent();
    }
  });
  
  if (window == top) {
    timer = setInterval(function() {
      try {
        document.documentElement.doScroll("left");
      } catch(e) { return; }
      fireContentLoadedEvent();
    }, 10);
  }
}

// WebKit builds lower than 525.13 don't support DOMContentLoaded
if (Prototype.Browser.WebKit && (userAgent.match(/AppleWebKit\/(\d+)/)[1] < 525)) {
  timer = setInterval(function() {
    if (/loaded|complete/.test(document.readyState))
      fireContentLoadedEvent();
  }, 10);
}

// Worst case fallback... 
Event.observe(window, "load", fireContentLoadedEvent); 
